package com.pocket.mic.servlet;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import com.pocket.mic.cache.CacheClient;
import com.pocket.mic.common.Action;
import com.pocket.mic.common.Constant;
import com.pocket.mic.common.Operation;
import com.pocket.mic.common.util.JSONUtil;
import com.pocket.mic.common.util.JsonPrint;
import com.pocket.mic.filter.SpringProxy;
import com.pocket.mic.model.Card;
import com.pocket.mic.model.Desk;
import com.pocket.mic.model.Game;
import com.pocket.mic.model.Player;
import com.pocket.mic.model.User;
import com.pocket.mic.service.GameService;
import com.pocket.mic.service.UserService;
public class ApiServlet extends HttpServlet {

	private static final long serialVersionUID = 7798981932422711342L;

	
	
		public void destroy() {
	        super.destroy(); 
	        // Just puts "destroy" string in log
	        // Put your code here
	    }

	    /**
	    	 * The doGet method of the servlet. <br>
	    	 *
	    	 * This method is called when a form has its tag value method equals to get.
	    	 * 
	    	 * @param request the request send by the client to the server
	    	 * @param response the response send by the server to the client
	    	 * @throws ServletException if an error occurred
	    	 * @throws IOException if an error occurred
	    	 */
	    public void doGet(HttpServletRequest request, HttpServletResponse response)
	                                                                               throws ServletException,
	                                                                               IOException {
	    	this.handle(request, response);
	    }

	    /**
	    	 * The doPost method of the servlet. <br>
	    	 *
	    	 * This method is called when a form has its tag value method equals to post.
	    	 * 
	    	 * @param request the request send by the client to the server
	    	 * @param response the response send by the server to the client
	    	 * @throws ServletException if an error occurred
	    	 * @throws IOException if an error occurred
	    	 */
	    public void doPost(HttpServletRequest request, HttpServletResponse response) {

	    	this.handle(request, response);
	    }

	    /**
	    	 * Initialization of the servlet. <br>
	    	 *
	    	 * @throws ServletException if an error occurs
	    	 */
	    public void init() throws ServletException {
	    }

//	    public void service(HttpServletRequest request,HttpServletResponse response){
//
//	    }
	    
	    private void handle(HttpServletRequest request, HttpServletResponse response){
	    	//TODO:text encrypt 

	    	GameService gameService=(GameService)SpringProxy.getBean("gameService");
	    	UserService userService=(UserService)SpringProxy.getBean("userService");
	    	
//	    	String sign=request.getParameter("sign");
//	    	String queryStr=request.getQueryString();
//	    	String userKey=request.getParameter("userKey");
	    	String op=request.getParameter("op");	    	
	    	String mac=request.getParameter("mac");
	    	String type=request.getParameter("type");
	    	String name=request.getParameter("name");
	    	String avatar=request.getParameter("avatar");
//	    	String pwd=request.getParameter("pwd");
	    	//action params
	    	String action=request.getParameter("action");
	    	String position=request.getParameter("pos");
	    	String mainPool=request.getParameter("mainPool");
	    	String maxBetNo=request.getParameter("maxBetNo");
	    	
	    	String sidePools_1=request.getParameter("sidePools_1");
	    	String sidePools_2=request.getParameter("sidePools_2");
	    	String sidePools_3=request.getParameter("sidePools_3");
	    	String sidePools_4=request.getParameter("sidePools_4");
	    	
	    	String bet=request.getParameter("bet");
	    	String chip=request.getParameter("chip");
	    	
	    	//game end
	    	String chips=request.getParameter("chips");
	    	
	    	HttpSession session=request.getSession();
	    	String returnCode="0";
			String returnMsg="done";
	    	PrintWriter pw=null;
	    	Map<String,String> rsMap=new HashMap<String,String>();
	    	try {
				pw=response.getWriter();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
	    	
	    	//step1:check sign
	    	
	    	//step2:check customer's op
	    	if(null!=op&&!"".equals(op)){
	    		int op_no=Integer.parseInt(op);
	    		if(Operation.OP_LOGIN!=op_no&&null==session.getAttribute(Constant.USER_NAME)){
					pw.append(JsonPrint.getJsonInfo("", "1", "not login!"));
					pw.flush();
					return;
	    		}
			switch (op_no) {
				case Operation.OP_HEART: {
					String gameKey=(String) request.getSession().getAttribute(Constant.GAME_KEY);
					heartBeat(pw,gameKey,session);
				}
					;
					break;
				case Operation.OP_LOGIN: {
					login(request, userService, mac, type, name, avatar,
							session, returnCode, returnMsg, pw, rsMap);
				}
					;
					break;
//				case Operation.OP_REGISTER: {
//					pw.append(JsonPrint.getJsonInfo("","0","register done"));
//				}
//					;
//					break;
				case Operation.OP_CREATE_DESK:{
					Game game=null;
					if(null!=session.getAttribute(Constant.GAME_KEY)){
						returnCode="1";
						returnMsg="already in a game!";
					}else{
						game = initGame(request,userService);
						
						if(null==game){
							returnCode="1";
							returnMsg="create desk failed!";
						}
						//response
					}
					try {
						pw.append(JsonPrint.getJsonInfo(JSONUtil.getJSON(game), returnCode, returnMsg));
					} catch (Exception e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				};break;
				case Operation.OP_JOIN_DESK:{
					joinDesk(request, gameService, userService, session,
							returnCode, returnMsg, pw,position);
					
				};break;
				case Operation.OP_GAME_START:{
					
					startGame(session, pw,gameService);
					
				};break;
				case Operation.OP_ACTION:{					
					String gameKey=(String)session.getAttribute(Constant.GAME_KEY);
					if(null!=gameKey&&null!=action){
						Game game=(Game)CacheClient.get(gameKey);
						try{
							game.setCurOperator(Integer.parseInt(session.getAttribute(Constant.POS).toString()));
						}catch(Exception e){
							e.printStackTrace();
						}
						int act=Integer.parseInt(action);
						//handle player 's action
						Long playerChip=0l;
						if(null!=chip&&!"".equals(chip)){
							playerChip=Long.parseLong(chip);
						}
						if(Action.ACTION_FOLLOW==act){
							actionUpdate(mainPool, maxBetNo, sidePools_1,sidePools_2,sidePools_3,sidePools_4, bet,playerChip,
									session, game,pw);
						}else if(Action.ACTION_LOOK==act){
							actionUpdate(mainPool, maxBetNo, sidePools_1,sidePools_2,sidePools_3,sidePools_4, bet,playerChip,
									session, game,pw);
						}else if(Action.ACTION_ADD==act){
							game.setIsAnybetAdded(1);
							actionUpdate(mainPool, maxBetNo, sidePools_1,sidePools_2,sidePools_3,sidePools_4, bet,playerChip,
									session, game,pw);
						}else if(Action.ACTION_OVER==act){
							actionUpdate(mainPool, maxBetNo, sidePools_1,sidePools_2,sidePools_3,sidePools_4, bet,playerChip,
									session, game,pw);
							for(Player player :game.getPlayers()){
								if(player.getUser().getName().equals(session.getAttribute(Constant.USER_NAME))){
									player.setDiscarded(true);
								}
							}
						}else if(Action.ACTION_STANDUP==act){
							Player tmpPlayer=null;
							for(Player player :game.getPlayers()){
								if(player.getUser().getName().equals(session.getAttribute(Constant.USER_NAME))){
									tmpPlayer=player;
								}
							}
							game.getPlayers().remove(tmpPlayer);
							game.getWaitingPlayers().add(tmpPlayer);
							
						}
//						heartBeat(pw,gameKey,session);
					}
				};break;
				case Operation.OP_GAME_END:{
					//game end and restart a game
					String gameKey=(String)session.getAttribute(Constant.GAME_KEY);
					if(null!=gameKey){
						Game game=(Game)CacheClient.get(gameKey);
						//update player 's chips
						if(null!=chips){
							String[] cch=chips.split(",");
							for(int i=0;i<cch.length;i++){
								if(null!=cch[i]&&!"".equals(cch[i])){
									Player player=game.getPlayers().get(i);
									player.setChip(Long.parseLong(cch[i]));
									
									//TODO:update user 's chips
									
								}
							}
						}
						game.setEndTime(new Date(System.currentTimeMillis()));
						//update game status to be end
						if(gameService.updateTime(game)){
							//restart a game
							Game newGame=new Game();						
//							newGame.setStartTime(new Date(System.currentTimeMillis()));
							newGame.setDesk(game.getDesk());
							newGame.setPlayers(game.getPlayers());
							newGame.setAlivePlayerCount(game.getAlivePlayerCount());
							newGame.setButtonNo((game.getButtonNo()+1)%game.getAlivePlayerCount());
							newGame.setStartNo(newGame.getButtonNo());
							newGame.setWaitingPlayers(game.getWaitingPlayers());
							
							Long gameId=gameService.addGame(newGame);
							newGame.setId(gameId);
							if(null!=gameId){
								CacheClient.add(gameKey, newGame);
								System.out.println("gameKey:"+gameKey);
								request.getSession().setAttribute(Constant.GAME_KEY, gameKey);
								
							}
							startGame(session, pw, gameService);
							
							heartBeat(pw,gameKey,session);
							
							//response
//							returnCode="0";
//							returnMsg="restart game done!";
							
						}else{
							returnCode="1";
							returnMsg="update game failed!";
							try {
								pw.append(JsonPrint.getJsonInfo("", returnCode, returnMsg));
							} catch (Exception e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}
						}
					}
					
				};break;
				case Operation.OP_SEND_MSG :{
					
				};break;
				case Operation.OP_CIRCLY_END:{
					//clear all bet
					String gameKey=(String)session.getAttribute(Constant.GAME_KEY);
					if(null!=gameKey){
						Game game=(Game)CacheClient.get(gameKey);
						for(Player player : game.getPlayers()){
							player.setBet(0);
						}
						game.setIsAnybetAdded(0);
						game.setCurrentround(game.getCurrentround()+1);
					}
				};break;
				default: {
				}
					;
				}
	    	}
	    	//step3:check data auth
	    	
	    	//step4:redirect request
	    	
	    	//step5:handle response string
	    	
	    	//step6:log
//	    	System.out.println("response ok!");
//	    	pw.append("\nresponse ok!");
	    	pw.flush();
	    }

		private void actionUpdate(String mainPool, String maxBetNo,
				String sidePools_1,String sidePools_2,String sidePools_3,String sidePools_4, String bet,Long chip, HttpSession session, Game game,PrintWriter pw) {
			String returnCode="0";
			String returnMsg="done";
			try{
				game.setCurOperator((Integer) session.getAttribute(Constant.POS));
				game.setMainPool(Integer.parseInt(mainPool));
				game.setMaxBetNo(Integer.parseInt(maxBetNo));
				for(Player player :game.getPlayers()){
					if(player.getUser().getName().equals(session.getAttribute(Constant.USER_NAME))){
						player.setChip(chip);
					}
				}
				//side pool 1
				String[] pools_1=sidePools_1.split(",");
				int[] sPools_1 = new int[pools_1.length];
				for(int i=0;i<pools_1.length;i++){
					sPools_1[i]=Integer.parseInt(pools_1[i]);
				}
				game.setSidePools_1(sPools_1);
				
				//side pool 2
				String[] pools_2=sidePools_2.split(",");
				int[] sPools_2 = new int[pools_2.length];
				for(int i=0;i<pools_2.length;i++){
					sPools_2[i]=Integer.parseInt(pools_2[i]);
				}
				game.setSidePools_2(sPools_2);
				//side pool 3
				String[] pools_3=sidePools_3.split(",");
				int[] sPools_3 = new int[pools_3.length];
				for(int i=0;i<pools_3.length;i++){
					sPools_3[i]=Integer.parseInt(pools_3[i]);
				}
				game.setSidePools_3(sPools_3);
				//side pool 4
				String[] pools_4=sidePools_4.split(",");
				int[] sPools_4 = new int[pools_4.length];
				for(int i=0;i<pools_4.length;i++){
					sPools_4[i]=Integer.parseInt(pools_4[i]);
				}
				game.setSidePools_4(sPools_4);
				
				game.setBet(Integer.parseInt(bet));
			}catch(Exception e){
				e.printStackTrace();
				
				returnCode="1";
				returnMsg="action params error!";
			}
			pw.append(JsonPrint.getJsonInfo("", returnCode, returnMsg));
		}

		private void login(HttpServletRequest request, UserService userService,
				String mac, String type, String name, String avatar,
				HttpSession session, String returnCode, String returnMsg,
				PrintWriter pw, Map<String, String> rsMap) {
			
			int firstLogin=0;
			
			if(null!=mac){
				if(null==name){
					name=mac;
				}
				
				User curUser=userService.getUserByName(name);
				
				if(null==curUser){
					firstLogin=1;
					User user=new User();
					if(null!=type){
						user.setType(Integer.parseInt(type));
					}
					user.setMac(mac);
					
					user.setName(name);
		//							user.setPwd(pwd);
					if(null!=avatar){
						user.setAvatar(avatar);
					}
					user.setRegTime(new Date(System.currentTimeMillis()));
					//save to db						
					boolean rs=userService.addUser(user);
					
					if(!rs){
						returnCode="1";
						returnMsg="login failed";
					}
				}
				if("0".equals(returnCode)){
					session.setAttribute(Constant.USER_NAME, name);
				}
			}else{
				returnCode="1";
				returnMsg="mac can't be null!";
			}
			rsMap.put(Constant.FIRST_LOGIN, String.valueOf(firstLogin));
			try {
				pw.append(JsonPrint.getJsonInfo(JSONUtil.getJSON(rsMap), returnCode, returnMsg));
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		private void startGame(HttpSession session, PrintWriter pw,GameService gameService) {
//			String returnCode;
//			String returnMsg;
			String gameKey=(String) session.getAttribute(Constant.GAME_KEY);
			Game game=(Game)CacheClient.get(gameKey);
			
			if(null!=game&&null!=game.getStartTime()){
//				try {
//					returnCode="1";
//					returnMsg="game started !";
//					pw.append(JsonPrint.getJsonInfo("", returnCode, returnMsg));
//				} catch (Exception e) {
//					// TODO Auto-generated catch block
//					e.printStackTrace();
//				}
			}else{
				game.setAlivePlayerCount(game.getPlayers().size());
				game.setBet(game.getDesk().getBaseChip());
				game.setCurOperator(0);
				game.setCurrentround(1);
				
				//init cards
				List<Card> cards=game.getCards();
				for(int i=0;i<5;i++){
					cards.add(game.giveCard());
				}
				game.setCards(cards);
				//int cards for players
				List<Player> players=game.getPlayers();
				for(Player player:players){
					List<Card> playerCards=player.getCards();
					for(int i=0;i<2;i++){
						playerCards.add(game.giveCard());
					}
				}
				Date now=new Date(System.currentTimeMillis());
				game.setStartTime(now);
				game.setEndTime(now);
				//update start time
				gameService.updateTime(game);
//				heartBeat(pw,gameKey,session);
			}
		}

		private void joinDesk(HttpServletRequest request,
				GameService gameService, UserService userService,
				HttpSession session, String returnCode, String returnMsg,
				PrintWriter pw,String position) {
			if(null!=session.getAttribute(Constant.GAME_KEY)){
				returnCode="1";
				returnMsg="already in a game!";
				pw.append(JsonPrint.getJsonInfo("", returnCode, returnMsg));
			}else{
				//TODO:get game key
				String gameKey=null;
				
				Game ga=gameService.getLiveGame();
				Game game=null;
				if(null!=ga){
					gameKey=getGameKey(ga);
					Object obj=CacheClient.get(gameKey);
					if(null!=obj){
						game=(Game) obj;
						ga=game;
						System.out.println("getLiveGame:"+ga.getId());
					}else{
						ga=null;
					}
				}else {
					ga=gameService.getWaitGame();
					if(null!=ga){
						gameKey=getGameKey(ga);
						Object ob=CacheClient.get(gameKey);
						if(null!=ob){
							game=(Game)ob;
						}else{
							ga=null;
						}
					}
				}
				//no available games create a new game
				if(null==ga){
					ga=initGame(request,userService);
//							game=ga;
//							CacheClient.add("game"+ga.getId(), ga);
					if(null==ga){
						returnCode="1";
						returnMsg="create desk failed!";
					}
					//response
					try {
						pw.append(JsonPrint.getJsonInfo(JSONUtil.getJSON(game), returnCode, returnMsg));
					} catch (Exception e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					
				}else{
					List<Player> players=game.getPlayers();
					int pos=players.size();
					if(null!=position){
						pos=Integer.parseInt(position);
					}
					Player player=new Player();
					player.setIdentity(Player.IDENTITY_BIGBLIND);
					User user=null;
					if(null!=session.getAttribute(Constant.USER_NAME)){
						user=userService.getUserByName((String)session.getAttribute(Constant.USER_NAME));
					}
//							User user=new User();
//							user.setId(new Long(pos+1));
//							user.setName("TestPlayer"+(pos+1));
					player.setUser(user);
					player.setLastRequestTime(System.currentTimeMillis());
					
					if(players.size() < game.getDesk().getPlayerMax()){
						//check the same player
						Player evilPlayer=null;
						for(Player pp :game.getPlayers()){
							if(pp.getUser().getName().equals(player.getUser().getName())){
								evilPlayer=pp;
								break;
							}
						}
						game.getPlayers().remove(evilPlayer);
						
						//game.getPlayers().add(pos,player);
						player.setSit(pos);
						game.getPlayers().add(player);
						
						session.setAttribute(Constant.POS, pos);
						
						//check the waiting players
						Player tmpPlayer=null;
						for(Player pp :game.getWaitingPlayers()){
							if(pp.getUser().getName().equals(player.getUser().getName())){
								tmpPlayer=pp;
								break;
							}
						}
						game.getWaitingPlayers().remove(tmpPlayer);
						
					}else if(players.size() < game.getDesk().getPlayerMax()*2){
						game.getWaitingPlayers().add(player);
					}else{
						returnCode="1";
						returnMsg="this desk is full!";
					}

					//update players info
					if(players.size()>0){
						game.setAlivePlayerCount(game.getPlayers().size());
						if(!gameService.updateLiveCount(game)){
							returnCode="1";
							returnMsg="update game error!";
						}
					}
					request.getSession().setAttribute(Constant.GAME_KEY, gameKey);
					
					System.out.println("alive count:"+game.getAlivePlayerCount());
					if(game.getAlivePlayerCount()>=2){
						startGame(session, pw, gameService);
					}
					
//						heartBeat(pw,gameKey,session);
					//response
					
					heartBeat(pw,gameKey,session);
					
//					try {
//						pw.append(JsonPrint.getJsonInfo(JSONUtil.getJSON(game), returnCode, returnMsg));
//					} catch (Exception e) {
//						// TODO Auto-generated catch block
//						e.printStackTrace();
//					}
				}
			}
		}

		public Game initGame(HttpServletRequest request,UserService userService) {
			GameService service=(GameService)SpringProxy.getBean("gameService");
			
			//init desk
			Desk desk=new Desk();
//			desk.setId(service.getMaxDesk());
			desk.setName("Test desk");
			desk.setStartTime(new Date(System.currentTimeMillis()));
			Long deskId=service.addDesk(desk);
			if(null!=deskId){
				desk.setId(deskId);
				//init game
				Game game=new Game();
				game.setDesk(desk);
				
//				game.setId(service.getMaxGame());
				game.setButtonNo(0);
				game.setAlivePlayerCount(1);
				game.setBet(desk.getBaseChip());
				game.setDesk(desk);
				game.setIsAnybetAdded(0);
				game.setMainPool(desk.getBaseChip());
				List<Player> players=game.getPlayers();
				//get current user's info
				Player player=new Player();
//				player.setIdentity(Player.IDENTITY_BUTTON);
				
				User user=userService.getUserByName((String)request.getSession().getAttribute(Constant.USER_NAME));
				player.setUser(user);		
				player.setSit(0);
				players.add(player);
				game.setPlayers(players);
				
				game.setStartNo(0);
				
				Long gameId=service.addGame(game);
				game.setId(gameId);
				
				if(null!=gameId){
					String gameKey = getGameKey(game);
					CacheClient.add(gameKey, game);
					System.out.println("gameKey:"+gameKey);
					request.getSession().setAttribute(Constant.GAME_KEY, gameKey);
					request.getSession().setAttribute(Constant.POS, 0);
					
					return game;
				}else{
					return null;
				}
			}else{
				return null;
			}
		}

		private String getGameKey(Game game) {
			String gameKey="game"+game.getDesk().getId();
			return gameKey;
		}
	    
	    private void heartBeat(PrintWriter pw,String gameKey,HttpSession session){
	    	System.out.println("heartbeat done!");
	    	if(null!=pw){
//	    		pw.append("heartbeat");
//	    		String gameKey=null;
	    		String returnCode="0";
	    		Game game=null;
	    		if(null!=gameKey){
	    			game=(Game)CacheClient.get(gameKey);
	    		}else{
	    			returnCode="1";
	    		}
	    		if(null!=game){
	    			killDeadPlayer(session, game);
	    		}
	    		try {
					pw.append(JsonPrint.getJsonInfo(JSONUtil.getJSON(game), returnCode, "heartbeat"));
				} catch (Exception e) {
					// TODO Auto-generated catch block
					pw.append(JsonPrint.getJsonInfo("", "1", "heartbeat"));
					e.printStackTrace();
				}
	    	}
	    }

		private void killDeadPlayer(HttpSession session, Game game) {
			Long now=System.currentTimeMillis();
			List<Player> tmpPlayers=new ArrayList<Player>();
			for(Player player:game.getPlayers()){
				String userName=(String) session.getAttribute(Constant.USER_NAME);
				if(null!=userName&&userName.equals(player.getUser().getName())){
					player.setLastRequestTime(now);
				}else{
					//dead player
					System.out.println("kill time: name-"+player.getUser().getName()+" player last time:"+player.getLastRequestTime());
					if(null==player.getLastRequestTime()||(now-player.getLastRequestTime())>Player.SLEEP_TIME){
						tmpPlayers.add(player);
					}
				}
			}
			for(Player p:tmpPlayers){
				game.getPlayers().remove(p);
				if(1<game.getAlivePlayerCount()){
					game.setAlivePlayerCount(game.getAlivePlayerCount()-1);
				}
				
			}
		}
}
